package cn.mw.cmdb.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.mw.cmdb.exception.FindNoException;
import cn.mw.microMonitorCommon.exception.BusinessException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import lombok.AllArgsConstructor;
import lombok.Data;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author swy
 * @description
 * @date 2024/4/10 20:10
 */
public class QrCodeUtil {

    private static final int QRCOLOR = 0xFF000000; // 默认是黑色
    private static final int BGWHITE = 0xFFFFFFFF; // 背景颜色

    /**
     * 生成二维码
     *
     * @param text   二维码内容
     * @param width  生成图片宽度
     * @param height 生成图片高度
     * @return
     */
    public static String createQrCodeBase64(String text, int width, int height) {
        try {
            QRCodeWriter writer = new QRCodeWriter();
            Hashtable hints = new Hashtable();
            hints.put(EncodeHintType.MARGIN, 1);
            BitMatrix bt = writer.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            MatrixToImageWriter.writeToStream(bt, "PNG", outputStream);
            return "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray());
        } catch (WriterException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static BufferedImage generateQRCode(String data) {
        try {
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.MARGIN, 0); // 设置二维码边距为0
            hints.put(EncodeHintType.ERROR_CORRECTION, com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.L); // 设置纠错级别为L
            //设置二维码尺寸为21像素
            BitMatrix matrix = new MultiFormatWriter().encode(data, BarcodeFormat.QR_CODE, 21, 21, hints);
            BufferedImage image = toBufferedImage(matrix);
            return image;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        // 将 BitMatrix 转换为 BufferedImage
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
            }
        }

        return image;
    }






    /**
     * 生成二维码
     *
     * @param text   二维码内容
     * @param width  生成图片宽度
     * @param height 生成图片高度
     * @return
     */
    public static BufferedImage getBufferedImage(String text, int width, int height) {
        try {
            File file = new File("D:/qr_code.png");
            QRCodeWriter writer = new QRCodeWriter();
            Hashtable hints = new Hashtable();
            hints.put(EncodeHintType.MARGIN, 1);
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            BitMatrix bt = writer.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bt);
            return bufferedImage;
        } catch (WriterException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 生成二维码 httpresponse使用如下
     * httpServletResponse.setContentType("application/zip");
     * httpServletResponse.setHeader("Content-disposition",
     * "attachment; filename=" + new String((date+"qr"+data.size()).getBytes(), "ISO-8859-1") + ".zip");
     *
     * @param qrCodes 二维码内容
     * @param width   生成图片宽度
     * @param height  生成图片高度
     * @return
     */
    public static void zipQrcode(List<QrCode> qrCodes, String logoPath, int width, int height, OutputStream outputStream) throws IOException {
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        try (InputStream logoStream = getFileInputStream(logoPath);) {
            BufferedImage logo = logoStream == null ? null : ImageIO.read(logoStream);
            qrCodes.forEach(code -> {
                String text = code.getText();
                String name = code.getName();
                try {
                    ZipEntry entry = new ZipEntry(name + "." + "JPG");
                    zipOutputStream.putNextEntry(entry);
                    createQrcodeWithNameAndLogo(code.getText(), code.getName(), logo, width, height, zipOutputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } catch (BusinessException e) {
            throw new FindNoException("logo_file_path_error");
        }

        zipOutputStream.close();
        outputStream.flush();
        outputStream.close();
    }

    private static InputStream getFileInputStream(String path) throws BusinessException {
        if (StrUtil.isBlank(path)) {
            return null;
        }
        if (path.startsWith("http://") || path.startsWith("https://")) {
            try {
                return URLUtil.getStream(new URL(path));
            } catch (MalformedURLException e) {
                throw new FindNoException("file_path_error");
            }
        }
        try {
            return new FileInputStream(path);
        } catch (FileNotFoundException e) {
            throw new FindNoException("file_path_error");
        }
    }

    /**
     * 生成二维码 httpresponse使用如下
     * httpServletResponse.setContentType("application/zip");
     * httpServletResponse.setHeader("Content-disposition",
     * "attachment; filename=" + new String((date+"qr"+data.size()).getBytes(), "ISO-8859-1") + ".zip");
     * 将多个二维码打包压缩为一个zip文件，并将压缩文件写入输出流
     *
     * @param qrCodes      二维码内容列表
     * @param logoPath     logo图片路径
     * @param outputStream 输出流
     * @throws IOException
     */
    public static void zipInspectionQrcode(List<QrCode> qrCodes, String logoPath, OutputStream outputStream) throws IOException {
        // 创建zip文件输出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        // 按照类型将二维码分组
        Map<String, List<QrCode>> collect = qrCodes.stream().collect(Collectors.groupingBy(QrCode::getType));
        // 遍历每个类型的二维码
        collect.forEach((type, codes) -> {
            // 加载类型对应的背景图
            InputStream bgimage = QrCodeUtil.class.getClassLoader().getResourceAsStream("images/" + type + ".png");
            if (bgimage == null) {
                // 抛出异常，背景图不存在
                throw new FindNoException("background_img_error");
            }
            try (InputStream logoStream = getFileInputStream(logoPath)) {
                // 加载logo图片
                BufferedImage logo = logoStream == null ? null : ImageIO.read(logoStream);
                // 加载背景图
                BufferedImage bg = ImageIO.read(bgimage);
                // 遍历该类型下的二维码
                codes.forEach(code -> {
                    String text = code.getText();
                    String name = code.getName();
                    try {
                        // 新建zip文件条目
                        ZipEntry entry = new ZipEntry(name + "." + "PNG");
                        zipOutputStream.putNextEntry(entry);
                        // 生成二维码并将生成的图片写入zip文件条目
                        createQrcode(code.getText(), code.getName(), bg, logo, zipOutputStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            } catch (IOException e) {
                // 抛出异常，二维码生成失败
                throw new FindNoException("create_qrcode_error");
            } catch (BusinessException e) {
                // 抛出异常，logo文件路径不正确
                throw new FindNoException("logo_file_path_error");
            }
        });
        // 关闭zip文件输出流
        zipOutputStream.close();
        // 刷新输出流
        outputStream.flush();
        // 关闭输出流
        outputStream.close();
    }

    public static void inspectionQrcode(String text, String name, String type, String logoPath, OutputStream outputStream) throws IOException {
        InputStream bgimage = QrCodeUtil.class.getClassLoader().getResourceAsStream("bgimages/" + type + ".png");
        if (bgimage == null) {
            throw new FindNoException("background_img_error");
        }
        try (InputStream logoStream = getFileInputStream(logoPath)) {
            BufferedImage logo = logoStream == null ? null : ImageIO.read(logoStream);
            BufferedImage bg = ImageIO.read(bgimage);
            createQrcode(text, name, bg, logo, outputStream);
        } catch (BusinessException e) {
            throw new FindNoException("logo_file_path_error");
        }

        outputStream.flush();
        outputStream.close();
    }

//    public static void createQrcode(String text, String name,BufferedImage bgimage, BufferedImage logo, OutputStream outputStream) {
//        try {
//            int qrWidth = 300;
//            int qrHeight = 300;
//
//            /**
//             * 1.生成二维码
//             */
//            QRCodeWriter writer = new QRCodeWriter();
//            Hashtable hints = new Hashtable();
//            hints.put(EncodeHintType.MARGIN, 1);
//            BitMatrix bt = writer.encode(text, BarcodeFormat.QR_CODE, qrWidth, qrHeight, hints);
//            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            MatrixToImageWriter.writeToStream(bt, "PNG", bos);
//            InputStream in = new ByteArrayInputStream(bos.toByteArray());
//            BufferedImage qrImage = ImageIO.read(in);
//            int w = 595;
//            int h = 842;
//            BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
//            Graphics2D graph = bufferedImage.createGraphics();
//            RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//            rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
//            rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
//            graph.setRenderingHints(rh);
//
//            graph.drawImage(bgimage,0,0,w,h,null);
//
//
//            /**
//             * 3.绘制二维码
//             */
//            graph.drawImage(qrImage, 147, 236, qrWidth, qrHeight, null);
//            /**
//             * 4.绘制logo,logo位于二维码中间位置，大小为二维码的1/5
//             */
//            if (logo != null) {
//                int logoWidth = qrWidth / 5;
//                int logoHeight = qrHeight / 5;
//                int logoX = 147 + (qrWidth - logoWidth) / 2;
//                int logoY = 236 + (qrHeight - logoHeight) / 2;
//                //画logo
//                graph.drawImage(logo, logoX, logoY, logoWidth, logoHeight, null);
//                //画圆角
//                graph.drawRoundRect(logoX, logoY, logoWidth, logoHeight, logoWidth / 5, logoHeight / 5);
//
//            }
//
//            Font font1 = new Font("微软雅黑", Font.PLAIN, 32);
//            graph.setFont(font1);
//            graph.setColor(Color.BLACK);
//            FontMetrics fm1 = graph.getFontMetrics();
//            int strLen1 = fm1.stringWidth(name);
//            int x = (w - strLen1) / 2;
//            int y = 576 + 26;
//            graph.drawString(name, x, y);
//
//
//            ImageIO.write(bufferedImage, "PNG", outputStream);
//        } catch (WriterException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }


    /**
     * 生成带logo和背景的二维码，并输出到输出流中
     *
     * @param text         二维码内容
     * @param name         姓名
     * @param bgimage      背景图片
     * @param logo         logo图片
     * @param outputStream 输出流
     */
    public static void createQrcode(String text, String name, BufferedImage bgimage, BufferedImage logo, OutputStream outputStream) {
        try {
            // 1.设置二维码的宽度和高度
            int qrWidth = 300;
            int qrHeight = 300;

            /**
             * 2.生成二维码
             */
            // 2.1 创建 QRCodeWriter 对象
            QRCodeWriter writer = new QRCodeWriter();
            // 2.2 设置二维码的边距
            Hashtable hints = new Hashtable();
            hints.put(EncodeHintType.MARGIN, 1);
            // 2.3 使用 QRCodeWriter 生成 BitMatrix 对象，表示二维码的矩阵
            BitMatrix bt = writer.encode(text, BarcodeFormat.QR_CODE, qrWidth, qrHeight, hints);
            // 2.4 将 BitMatrix 对象写入 ByteArrayOutputStream 中
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            MatrixToImageWriter.writeToStream(bt, "PNG", bos);
            // 2.5 将 ByteArrayOutputStream 转换为 ByteArrayInputStream
            InputStream in = new ByteArrayInputStream(bos.toByteArray());
            // 2.6 读取 ByteArrayInputStream 中的数据，生成二维码图片
            BufferedImage qrImage = ImageIO.read(in);

            // 3.设置背景图片
            int w = 595;
            int h = 842;
            BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Graphics2D graph = bufferedImage.createGraphics();
            // 3.1 设置绘图相关参数
            RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
            graph.setRenderingHints(rh);
            // 3.2 绘制背景图片
            graph.drawImage(bgimage, 0, 0, w, h, null);

            /**
             * 4.绘制二维码
             */
            // 4.1 将二维码图片绘制到背景图片上
            graph.drawImage(qrImage, 147, 236, qrWidth, qrHeight, null);

            /**
             * 5.绘制logo,logo位于二维码中间位置，大小为二维码的1/5
             */
            if (logo != null) {
                int logoWidth = qrWidth / 5;
                int logoHeight = qrHeight / 5;
                int logoX = 147 + (qrWidth - logoWidth) / 2;
                int logoY = 236 + (qrHeight - logoHeight) / 2;
                // 5.1 绘制 logo 图片
                graph.drawImage(logo, logoX, logoY, logoWidth, logoHeight, null);
                // 5.2 绘制圆角
                graph.drawRoundRect(logoX, logoY, logoWidth, logoHeight, logoWidth / 5, logoHeight / 5);
            }

            // 5.3 添加文本
            Font font1 = new Font("微软雅黑", Font.PLAIN, 32);
            graph.setFont(font1);
            graph.setColor(Color.BLACK);
            FontMetrics fm1 = graph.getFontMetrics();
            int strLen1 = fm1.stringWidth(name);
            int x = (w - strLen1) / 2;
            int y = 576 + 26;
            graph.drawString(name, x, y);

        // 6 将BufferedImage写出到输出流中
            ImageIO.write(bufferedImage, "PNG", outputStream);

        } catch (WriterException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void createQrcodeWithNameAndLogo(String text, String name, BufferedImage logo, int width, int height, OutputStream outputStream) {
        try {
            /**
             * 1.生成二维码
             */
            QRCodeWriter writer = new QRCodeWriter();
            Hashtable hints = new Hashtable();
            hints.put(EncodeHintType.MARGIN, 1);
            BitMatrix bt = writer.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
            BufferedImage qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            // 开始利用二维码数据创建Bitmap图片，分别设为黑（0xFFFFFFFF）白（0xFF000000）两色
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    qrImage.setRGB(x, y, bt.get(x, y) ? QRCOLOR : BGWHITE);
                }
            }
            /**
             * 2.绘制二维码图片，使用和二维码一样的宽度，高度比二维码高度高出50px
             */
            int w = width;
            int h = height + 50;
            BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D graph = bufferedImage.createGraphics();
            RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
            graph.setRenderingHints(rh);
            /**
             * 3.绘制二维码
             */
            graph.drawImage(qrImage, 0, 0, width, height, null);
            /**
             * 4.绘制logo,logo位于二维码中间位置，大小为二维码的1/5
             */
//            if (logo != null) {
//                int logoWidth = width / 5;
//                int logoHeight = height / 5;
//                int logoX = (w - logoWidth) / 2;
//                int logoY = (h - logoHeight) / 2;
//                //画logo
//                graph.drawImage(logo, logoX, logoY, logoWidth, logoHeight, null);
//                //画圆角
//                graph.drawRoundRect(logoX, logoY, logoWidth, logoHeight, logoWidth / 5, logoHeight / 5);
//
//            }
            /**
             * 5.文字区域底色留白
             */
            for (int x = 0; x < width; x++) {
                for (int y = height; y < h; y++) {
                    bufferedImage.setRGB(x, y, BGWHITE);
                }
            }
            Font font = new Font("宋体", Font.PLAIN, 18);
            graph.setFont(font);
            graph.setColor(Color.BLACK);
            FontMetrics fm = graph.getFontMetrics();
            int strLen = fm.stringWidth(name);
            int x = (width - strLen) / 2;
            int ascent = fm.getAscent();
            int descent = fm.getDescent();
            int y = height + (50 - (ascent + descent)) / 2 + ascent;
            graph.setBackground(Color.WHITE);
            graph.drawString(name, x, y);
            ImageIO.write(bufferedImage, "PNG", outputStream);
        } catch (WriterException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Data
    @AllArgsConstructor
    public static class QrCode {
        private String text;
        private String name;
        private String type;
    }

    public static void main(String[] args) throws IOException {
        List<QrCode> qrCodes = new ArrayList<>();
        Long milles = System.currentTimeMillis();
        IntStream.range(3534359, 3534379).forEach(i -> {
            qrCodes.add(new QrCode("http://www.baidu.com?wd=好几十大厦世纪东方挥洒 发哈市接口返回科技发挥了看法加拉克法兰克福家发了节流阀考虑就加速度快就" + i, "" + i, i % 3 == 0 ? "点位码" : "设备码"));
        });
//        FileOutputStream outputStream = new FileOutputStream("D:\\tmp1\\a.png");
//        inspectionQrcode(qrCodes.get(0).getText(),qrCodes.get(0).getName(),"设备码", "E:\\Downloads\\6695e0fa23c91467b0aa463a5b4a35b3.jpeg",  outputStream);
        FileOutputStream outputStream = new FileOutputStream("D:\\tmp1\\a.zip");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        zipInspectionQrcode(qrCodes, "E:\\Downloads\\6695e0fa23c91467b0aa463a5b4a35b3.jpeg", outputStream);
        Long end = System.currentTimeMillis();
        System.out.println(end - milles);
//        String base64 = createQrCodeBase64("https://openauth.alipaydev.com/oauth2/publicAppAuthorize.htm?app_id=2016080200152244&scope=auth_user&redirect_uri=http%3A%2F%2Ftuchuan2014.vicp.cc%2Fagent%2Falipay%2Fauth%3FuserId%3D10034%26timestamp%3D1632815746625%26sign%3D98810b9e07b164f9251f1df028770e79", 300, 300);
//        System.out.println(base64);
    }
}
